
#ifndef SHADER_DEFS_H
#define SHADER_DEFS_H

#define MAX_LIGHTS 3
#define INVERSE_4PI 0.07957747154594766788444188168626f

//#define ZBUFFER

// Next section is solely for shaders (C/C++ compilers must define __TIMESTAMP__)
// This way it's possible to include this file in both, shaders and c++ code
#ifndef __TIMESTAMP__
uniform Matrices {
    mat4 Projection;
    mat4 Camera;
} GlobalMatrices;

uniform Model {
    mat4 ModelView;
    mat3 NormalM;
} LocalModel;

uniform MaterialAttrs {
    vec4 diffuse;
    vec4 extra;
} Material;

struct Light {
    vec3 pos;
    vec4 color;    
};

uniform Lights {
    Light lights[MAX_LIGHTS];
} GlobalLights;

const vec3 cEye=vec3(0,0,0);

vec3 Lighting(const in vec3 normal, const in vec3 location, const in vec4 material, const in float roughness)
{
    vec3 outColor=vec3(0,0,0);
    vec3 view=normalize(cEye-location);
    for(int i=0; i<MAX_LIGHTS; i++)
        if(GlobalLights.lights[i].color.a > 0) {
            vec3 incoming=location-GlobalLights.lights[i].pos;
            float aten=inversesqrt(dot(incoming,incoming));
            incoming*=aten;
            aten*=aten*INVERSE_4PI*GlobalLights.lights[i].color.a;
            outColor-=min(0, dot(incoming, normal))*GlobalLights.lights[i].color.rgb*aten*material.rgb;
            // Obtain <N,H>
            vec3 reflected=reflect(incoming, normal);
            float spec=(dot(reflected, view)+1)*0.5;
            float smoothness=1/(roughness*roughness);
            spec=1/spec;
            // compute the coefficient
            spec=exp((1-spec)*smoothness)*smoothness*spec*spec*INVERSE_4PI*4;
            // nullify in case when not facing the light
            spec*=(sign(dot(reflected,normal))+1)*0.5;
            outColor+=spec*GlobalLights.lights[i].color.rgb*aten*material.a;
        }
        return outColor;
}

vec4 ProjectVertex(const in mat4 Projection, const in vec4 Position)
{
    vec4 pPos=Projection*Position;
#ifndef ZBUFFER
    // wbuffer
    pPos.z*=pPos.w;
#endif
    return pPos;
}

vec4 UnProjectVertex(const in mat4 InvProjection, const in vec3 ndcPos, const in vec2 NearFar)
{
    vec4 pos;
    pos.xyz=ndcPos;
#ifndef ZBUFFER
    pos.w=-0.5f*dot(NearFar, vec2(1-ndcPos.z, 1+ndcPos.z));
    pos.xy*=pos.w;
#else
    pos.w=-2.0f*NearFar.x*NearFar.y/dot(NearFar, vec2(1+ndcPos.z, 1-ndcPos.z));
    pos.xyz*=pos.w;
#endif
    return InvProjection*pos;
}

// __TIMESTAMP__
#endif

// SHADER_DEFS_H
#endif